home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 14439 / 14439.xpi / chrome / tabber.jar / content / multirow.js < prev    next >
Text File  |  2009-11-12  |  14KB  |  392 lines

  1. /*
  2.  
  3. Multiple-row tab bar
  4. Reference: Tab Kit by John Mellor
  5. Latest revisions by Frank Yan - 2009.10.12
  6.  
  7. <license>
  8.  
  9. Tab Kit - http://jomel.me.uk/software/firefox/tabkit/
  10. Copyright (c) 2007 John Mellor
  11.  
  12. This Firefox extension, Tab Kit, is free software; you can
  13. redistribute it and/or modify it under the terms of the GNU
  14. General Public License as published by the Free Software
  15. Foundation; either version 2 of the License, or (at your
  16. option) any later version.
  17.  
  18. Tab Kit is distributed in the hope that it will be useful,
  19. but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. GNU General Public License for more details.
  22.  
  23. You should have received a copy of the GNU General Public License
  24. along with this program. If not, see <http://www.gnu.org/licenses/>.
  25.  
  26. </license>
  27.  
  28. */
  29.  
  30. var multirow = new function _multirow() {
  31.     const mr = this;    
  32.  
  33.     var _tabContainer;
  34.     var _tabstrip;
  35.     var _tabInnerBox;
  36.     var _tabs;
  37.     var _prefObservers = {};
  38.     var initialized = false;
  39.  
  40.     var appTabWidth = 30;
  41.  
  42.     this.preInit = function preInit() {
  43.         mr.initialized = true;
  44.         mr.preInitUAStyleSheets();
  45.         mr.preInitFixDropIndex();
  46.     };
  47.  
  48.     this.init = function init(event) {
  49.         _tabInnerBox.style.setProperty("opacity", "0", "important");
  50.         setTimeout(function() { multirow.redraw(); }, 0);
  51.         mr.initMultiRowTabs();
  52.     };
  53.  
  54.     this.initPrefCheck = function initPrefCheck() {
  55.         _tabContainer = gBrowser.mTabContainer;
  56.         _tabstrip = _tabContainer.mTabstrip;
  57.         _tabInnerBox = document.getAnonymousElementByAttribute(_tabstrip._scrollbox, "class", "box-inherit scrollbox-innerbox");
  58.         _tabs = gBrowser.mTabs;
  59.  
  60.         mr.initTabMinWidth();
  61.         mr.addPrefListener("tabberwocky.multirow", mr.lateMultiRow);
  62.     };
  63.  
  64.     this.UAStyleSheets = [ "chrome://tabber/content/scrollbar.css" ];
  65.  
  66.     this.preInitUAStyleSheets = function preInitUAStyleSheets() {    // [Fx3only] it seems
  67.         var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
  68.         var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  69.         for each (var s in mr.UAStyleSheets) {
  70.             var uri = ios.newURI(s, null, null);
  71.             if (!sss.sheetRegistered(uri, sss.AGENT_SHEET))
  72.                 sss.loadAndRegisterSheet(uri, sss.AGENT_SHEET);
  73.         }
  74.     };
  75.  
  76.     this.lateMultiRow = function lateMultiRow() {
  77.         if (!gBrowser.mPrefs.getBoolPref("tabberwocky.multirow")) return;
  78.         if (mr.initialized) return;
  79.         mr.preInit();
  80.         mr.initMultiRowTabs();
  81.     };
  82.  
  83.     this.initTabMinWidth = function initTabMinWidth() {
  84.         mr.addPrefListener("browser.tabs.tabMinWidth", mr.resetTabMinWidth);
  85.     };
  86.     this.resetTabMinWidth = function resetTabMinWidth(pref) {
  87.         mr.setTabMinWidth(gPrefService.getIntPref("browser.tabs.tabMinWidth"));
  88.     };
  89.     this.setTabMinWidth = function setTabMinWidth(minWidth) {
  90.         _tabContainer.mTabMinWidth = minWidth;
  91.         for (var i = 0; i < _tabs.length; i++)
  92.             if (_tabs[i].getAttribute("apptab") != "true")
  93.                 _tabs[i].minWidth = minWidth;
  94.             else
  95.                 _tabs[i].minWidth = _tabContainer.getAttribute("multirow") == "true" ? mr.appTabWidth :
  96.                     (appTabs && appTabs.minWidth ? appTabs.minWidth : 0);
  97.         _tabContainer.adjustTabstrip();
  98.     };
  99.  
  100.     this.addPrefListener = function addPrefListener(prefString, prefListener) {
  101.         if (!_prefObservers[prefString]) {
  102.             _prefObservers[prefString] = {
  103.                 listeners: [],
  104.  
  105.                 register: function() {
  106.                     gPrefService.addObserver(prefString, this, false);
  107.                 },
  108.  
  109.                 unregister: function() {
  110.                     gPrefService.removeObserver(prefString, this);
  111.                 },
  112.  
  113.                 observe: function(aSubject, aTopic, aData) {
  114.                     if (aTopic != "nsPref:changed") return;
  115.                     for each (var listener in this.listeners) {
  116.                         listener(aData);
  117.                     }
  118.                 }
  119.             };
  120.  
  121.             window.addEventListener("unload", function() { _prefObservers[prefString].unregister(); }, false);
  122.             _prefObservers[prefString].register();
  123.         }
  124.  
  125.         _prefObservers[prefString].listeners.push(prefListener);
  126.     };
  127.  
  128.     this.scrollToElement = function scrollToElement(overflowPane, element) {
  129.         if (_tabContainer.getAttribute("multirowscroll") != "true") return;
  130.         var scrollbar = overflowPane.mVerticalScrollbar;
  131.         if (!scrollbar) return;
  132.  
  133.         var container = element.parentNode;
  134.         var firstChild = container.firstChild;
  135.         while (firstChild.hidden || 
  136.         document.defaultView.getComputedStyle(firstChild, null).getPropertyValue("visibility") != "visible")
  137.             firstChild = firstChild.nextSibling;
  138.         var lastChild = container.lastChild;
  139.         while (lastChild.hidden || 
  140.         document.defaultView.getComputedStyle(lastChild, null).getPropertyValue("visibility") != "visible")
  141.             lastChild = lastChild.previousSibling;
  142.  
  143.         var curpos = parseInt(scrollbar.getAttribute("curpos"));
  144.         if (isNaN(curpos)) curpos = 0;
  145.         var firstY = firstChild.boxObject.y;
  146.         var elemY = element.boxObject.y;
  147.         var lastY = lastChild.boxObject.y;
  148.         var height = element.boxObject.height;
  149.         var relY = elemY - firstY;
  150.         var paneHeight = overflowPane.boxObject.height;
  151.  
  152.         // Make sure overflowPane is never scrolled halfway across elements at both the top and bottom
  153.         if ((lastY - firstY) % height == 0 && curpos % height != 0 && (curpos + paneHeight + firstY - lastY) % height != 0)
  154.             curpos = height * Math.round(curpos / height);
  155.  
  156.         var minpos = relY;
  157.         if (minpos < curpos) curpos = minpos;
  158.         else {
  159.             var maxpos = relY + height - paneHeight;
  160.             if (maxpos > curpos) curpos = maxpos;
  161.         }
  162.  
  163.         scrollbar.setAttribute("curpos", curpos);
  164.     };
  165.  
  166.     /// Initialisation:
  167.     this.initMultiRowTabs = function initMultiRowTabs() {                
  168.         mr.addPrefListener("tabberwocky.multirow", mr.updateMultiRowTabs);
  169.         mr.addPrefListener("tabberwocky.maxrows", mr.updateMultiRowTabs);
  170.         mr.addPrefListener("tabberwocky.newtabbutton", mr.updateMultiRowTabs);
  171.         mr.addPrefListener("browser.tabs.tabMinWidth", mr.updateMultiRowTabs);
  172.         mr.addPrefListener("browser.tabs.closeButtons", mr.delayedUpdate);
  173.  
  174.         _tabstrip.addEventListener("overflow", mr._preventMultiRowFlowEvent, true);
  175.         _tabstrip.addEventListener("underflow", mr._preventMultiRowFlowEvent, true);
  176.  
  177.         _tabContainer.addEventListener("TabOpen", mr.updateMultiRowTabs, false);
  178.         _tabContainer.addEventListener("TabClose", mr.delayedUpdate, false);
  179.         document.addEventListener("SSTabRestoring", mr.updateMultiRowTabs, false); // "hidden" attributes might be restored!
  180.         window.addEventListener("resize", mr.updateMultiRowTabs, false);
  181.  
  182.         _tabContainer.addEventListener("TabSelect", mr.multiRow_onTabSelect, false);
  183.         _tabContainer.addEventListener("TabMove", mr.multiRow_onTabSelect, false); // In case a tab is moved out of sight
  184.  
  185.         mr.updateMultiRowTabs();
  186.     };
  187.     
  188.     this.delayedUpdate = function delayedUpdate() {
  189.         setTimeout(function() { multirow.updateMultiRowTabs(); }, 0);
  190.     };
  191.  
  192.     this.redraw = function redraw() {
  193.         _tabInnerBox.style.setProperty("opacity", "1", "important");
  194.         _tabInnerBox.removeAttribute("style");
  195.         if (_tabContainer.getAttribute("multirow") == "true")
  196.             setTimeout(function() { multirow.multiRow_onTabSelect(); }, 0);
  197.     };
  198.  
  199.     /// Event Listeners:
  200.     this.updateMultiRowTabs = function updateMultiRowTabs() {
  201.         var needsDisabling = false, needsScrolling = false;
  202.         if (gBrowser.mPrefs.getBoolPref("tabberwocky.multirow")) {
  203.             _tabContainer.removeAttribute("overflow");
  204.             _tabContainer.setAttribute("newatend", "true");
  205.             if (!gBrowser.getStripVisibility()) {
  206.                 var rows = 0;
  207.             }
  208.             else {
  209.                 var visibleTabs = _tabs.length, apps = 0, firstApp = -1;
  210.                 for (var i = 0; i < _tabs.length; i++) {
  211.                     if (_tabs[i].hidden)
  212.                         visibleTabs--;
  213.                     if (_tabs[i].getAttribute("apptab") == "true") {
  214.                         if (firstApp == -1) {
  215.                             firstApp = i;
  216.                             if (!mr.appTabWidth) {
  217.                                 var f = document.getAnonymousElementByAttribute(_tabs[i], "class", "tab-icon-image");
  218.                                 mr.appTabWidth = 16 +
  219.                                     mr.getIntStyle(f, "padding-left") + mr.getIntStyle(f, "padding-right") +
  220.                                     mr.getIntStyle(f, "margin-left") + mr.getIntStyle(f, "margin-right") +
  221.                                     mr.getIntStyle(_tabs[i], "padding-left") + mr.getIntStyle(_tabs[i], "padding-right") +
  222.                                     mr.getIntStyle(_tabs[i], "border-left-width") + mr.getIntStyle(_tabs[i], "border-right-width");
  223.                                 if (mr.appTabWidth < 30) mr.appTabWidth = 30;
  224.                             }
  225.                         }
  226.                         apps++;
  227.                     }
  228.                 }
  229.                 var availWidth = _tabstrip._scrollbox.boxObject.width;
  230.                 availWidth -= mr.getIntStyle(_tabstrip._scrollbox, "padding-left");
  231.                 availWidth -= mr.getIntStyle(_tabstrip._scrollbox, "padding-right");
  232.                 var minWidth = gPrefService.getIntPref("browser.tabs.tabMinWidth");
  233.                 var tabsPerRow = Math.floor(availWidth / minWidth);
  234.                 var tab = (_tabs.length > 1) ? 1 : 0;
  235.                 var margin = mr.getIntStyle(_tabs[tab], "margin-left") + mr.getIntStyle(_tabs[tab], "margin-right");
  236.                 while (tabsPerRow * (minWidth + margin) > availWidth)
  237.                     tabsPerRow--;
  238.                 if (margin < 0) margin /= 2;
  239.                 if (apps) {
  240.                     var w = (availWidth - (margin * tabsPerRow)) / tabsPerRow;
  241.                     if (minWidth > w) w = minWidth;
  242.                     var t = Math.floor((w - mr.appTabWidth) * apps / w);
  243.                     if (t > 0)
  244.                         visibleTabs -= t;
  245.                 }
  246.                 if (margin < 0) visibleTabs -= margin / minWidth;
  247.                 var rows = Math.ceil(visibleTabs / tabsPerRow);
  248.             }
  249.             if (rows > 1) {
  250.                 if (_tabContainer.getAttribute("multirow") != "true") {
  251.                     _tabContainer.setAttribute("multirow", "true");
  252.                     gBrowser.mTabBox.setAttribute("dropindicator-hidden", "true");
  253.                     try {
  254.                         _tabstrip._scrollBoxObject.scrollTo(0, 0);
  255.                     }
  256.                     catch (ex) {}
  257.                 }
  258.  
  259.                 var maxRows = gBrowser.mPrefs.getIntPref("tabberwocky.maxrows");
  260.                 if (rows > maxRows) {
  261.                     _tabContainer.setAttribute("multirowscroll", "true");
  262.  
  263.                     // make sure tab borders and padding are properly taken into account...
  264.                     _tabstrip.style.setProperty("min-height", 24 * maxRows + "px", "important");
  265.                     _tabstrip.style.setProperty("max-height", 24 * maxRows + "px", "important");
  266.  
  267.                     var scrollbar = _tabInnerBox.mVerticalScrollbar;
  268.                     try {
  269.                         scrollbar.removeEventListener("DOMAttrModified", mr.preventChangeOfAttributes, true);
  270.                     }
  271.                     catch (ex) {}
  272.                     try {
  273.                         scrollbar.setAttribute("increment", 24);
  274.                         scrollbar.setAttribute("pageincrement", 48);
  275.                         scrollbar.addEventListener("DOMAttrModified", mr.preventChangeOfAttributes, true);
  276.  
  277.                         availWidth -= Math.max(scrollbar.boxObject.width, 22);
  278.                     }
  279.                     catch (ex) { // scrollbar not yet created
  280.                         availWidth -= 22;
  281.                     }
  282.                 }
  283.                 else {
  284.                     _tabContainer.removeAttribute("multirowscroll");
  285.  
  286.                     _tabstrip.style.setProperty("min-height", 24 * rows + "px", "important");
  287.                     _tabstrip.style.setProperty("max-height", 24 * rows + "px", "important");
  288.                 }
  289.  
  290.                 mr.setTabMinWidth((availWidth - (margin * tabsPerRow)) / tabsPerRow);
  291.  
  292.                 if (rows > maxRows)
  293.                     mr.multiRow_onTabSelect(); // check if we need to scroll
  294.  
  295.                 // check if we need to redraw
  296.                 if (_tabInnerBox.boxObject.screenY != _tabInnerBox.parentNode.boxObject.screenY) {
  297.                     _tabInnerBox.setAttribute("style", "display:inline !important");
  298.                     setTimeout(function() { multirow.redraw(); }, 0);
  299.                 }
  300.             }
  301.             else {
  302.                 if (_tabContainer.getAttribute("multirow") == "true") {
  303.                     needsDisabling = true;
  304.                     needsScrolling = true;
  305.                 }
  306.                 _tabContainer.setAttribute("multirow", "false");
  307.             }
  308.         }
  309.         else if (_tabContainer.hasAttribute("multirow")) {
  310.             _tabContainer.removeAttribute("multirowscroll");
  311.             _tabContainer.removeAttribute("newatend");
  312.             needsDisabling = true;
  313.             needsScrolling = (_tabContainer.getAttribute("multirow") == "true");
  314.             _tabContainer.removeAttribute("multirow");
  315.         }
  316.  
  317.         if (needsDisabling) {
  318.             gBrowser.mTabBox.removeAttribute("dropindicator-hidden");
  319.  
  320.             mr.resetTabMinWidth();
  321.  
  322.             if (needsScrolling) {
  323.                 try {
  324.                     _tabstrip._scrollBoxObject.ensureElementIsVisible(gBrowser.selectedTab);
  325.                 }
  326.                 catch (ex) {}
  327.             }
  328.  
  329.             _tabstrip.style.removeProperty("min-height");
  330.             _tabstrip.style.removeProperty("max-height");
  331.  
  332.             _tabContainer.adjustTabstrip();
  333.         }
  334.     };
  335.  
  336.     this.getIntStyle = function getIntStyle(node, property) {
  337.         return parseInt(document.defaultView.getComputedStyle(node, null).getPropertyValue(property));
  338.     };
  339.  
  340.     this.preventChangeOfAttributes = function preventChangeOfAttributes(event) {
  341.         var scrollbar = _tabInnerBox.mVerticalScrollbar;
  342.         if (event.attrName == "increment") {
  343.             scrollbar.setAttribute("increment", 24);
  344.             event.stopPropagation();
  345.         }
  346.         else if (event.attrName == "pageincrement") {
  347.             scrollbar.setAttribute("pageincrement", 48);
  348.             event.stopPropagation();
  349.         }
  350.     };
  351.  
  352.     this.multiRow_onTabSelect = function multiRow_onTabSelect() {
  353.         mr.scrollToElement(_tabInnerBox, gBrowser.selectedTab);
  354.     };
  355.  
  356.     /// Method hooks:
  357.     this.preInitFixDropIndex = function preInitFixDropIndex(event) {
  358.         tkLib.addMethodHook([
  359.         "gBrowser.getNewIndex",
  360.         null,
  361.         '{',
  362.         '{ \
  363.         var multiRow = this.mTabContainer.getAttribute("multirow") == "true";',
  364.  
  365.         'aEvent.screenX < this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2', // ltr
  366.         '(multiRow && aEvent.screenY < this.mTabs[i].boxObject.screenY) \
  367.             || (aEvent.screenX < this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2 \
  368.             && (aEvent.screenY < this.mTabs[i].boxObject.screenY + this.mTabs[i].boxObject.height \
  369.             || !multiRow))',
  370.  
  371.         'aEvent.screenX > this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2', // rtl
  372.         '(multiRow && aEvent.screenY < this.mTabs[i].boxObject.screenY) \
  373.             || (aEvent.screenX > this.mTabs[i].boxObject.screenX + this.mTabs[i].boxObject.width / 2 \
  374.             && (aEvent.screenY < this.mTabs[i].boxObject.screenY + this.mTabs[i].boxObject.height \
  375.             || !multiRow))'
  376.     ]);
  377.     };
  378.  
  379.     /// Private Methods
  380.     this._preventMultiRowFlowEvent = function _preventMultiRowFlowEvent(event) {
  381.         if (_tabContainer.hasAttribute("multirow")) {
  382.             event.preventDefault();
  383.             event.stopPropagation();
  384.         }
  385.     };
  386. };
  387.  
  388. window.addEventListener("load", multirow.initPrefCheck, false);
  389. if (gPrefService.getBoolPref('tabberwocky.multirow')) {
  390.     window.addEventListener("DOMContentLoaded", multirow.preInit, false);
  391.     window.addEventListener("load", multirow.init, false);
  392. }